/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 adv_actor_model_cache.cpp

	$Header: /heroes4/adv_actor_model_cache.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 ) // Disable the ever so helpful "identifier was truncated to '255' characters" warning

#include "adv_actor_model_cache.h"

#include <cassert>
#include <map>
#include <memory>
#include <utility>

#include "adv_actor_model.h"
#include "adv_actor_model_definition.h"
#include "cache_base.h"
#include "creature_type.h"
#include "enum_operations.h"
#include "owned_ptr.h"
#include "hero_class_properties.h"
#include "ptr_cache.h"
#include "resource_dir.h"
#include "shared_ptr.h"
#include "string_insensitive_compare.h"
#include "town_type.h"

// ------------------------------------------------------------------------------
// Unnamed namespace
// ------------------------------------------------------------------------------

namespace
{

	// ------------------------------------------------------------------------------
	// t_cache class
	// ------------------------------------------------------------------------------

	class t_cache_data : public t_abstract_cache_data< t_adv_actor_model >, public t_cache_base
	{
	public:
		// Types
		typedef t_pointer_cache< t_adv_actor_model_definition > t_definition_cache;

		// Constructor
		t_cache_data( std::string const & name );

		virtual int get_size();
	private:

		// Member functions
		virtual void				add_reference();
		virtual t_adv_actor_model *	do_get( t_progress_handler* handler );
		virtual void                release_memory();
		virtual void				remove_reference();

		// Data members
		t_definition_cache					m_definition_cache;
		t_owned_ptr< t_adv_actor_model >	m_adv_actor_model_ptr;
	};

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_cache_data::t_cache_data( std::string const & name )
		:	m_definition_cache( name )
	{
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_cache_data::add_reference()
	{
		t_cache_base::add_reference();
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_adv_actor_model * t_cache_data::do_get( t_progress_handler* handler )
	{
		if ( m_adv_actor_model_ptr.get() == 0 )
		{
			t_cached_ptr< t_adv_actor_model_definition > definition_ptr = m_definition_cache.get();
			if ( definition_ptr.get() == 0 )
				return 0;

			m_adv_actor_model_ptr.reset( new t_adv_actor_model( definition_ptr ) );
		}
		return m_adv_actor_model_ptr.get();
	}

	int t_cache_data::get_size()
	{
		return sizeof( t_adv_actor_model );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_cache_data::remove_reference()
	{
		t_cache_base::remove_reference();
	}

	void t_cache_data::release_memory()
	{
		m_adv_actor_model_ptr.reset();
	}

	class t_cache : public t_abstract_cache< t_adv_actor_model >
	{
	public:
		t_cache( std::string const& name );
	};

	inline t_cache::t_cache( std::string const& name )
		          : t_abstract_cache<t_adv_actor_model>( new t_cache_data( name ) )
	{
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	typedef std::map< std::string, t_shared_ptr< t_cache >, t_string_insensitive_less > 
		t_cache_ptr_map;

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	inline t_cache_ptr_map & get_cache_ptr_map()
	{
		static t_cache_ptr_map cache_ptr_map;
		return cache_ptr_map;
	}

} // Unnamed namespace

// ------------------------------------------------------------------------------
// Details namespace
// ------------------------------------------------------------------------------

namespace adv_actor_model_cache_details
{

	// ------------------------------------------------------------------------------
	// t_initializer members
	// ------------------------------------------------------------------------------
	t_initializer::t_initializer()
	{
		get_cache_ptr_map();
	}

} // Details namespace

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_abstract_cache< t_adv_actor_model > & get_adv_actor_model_cache( std::string const & name )
{
	t_cache_ptr_map & cache_ptr_map = get_cache_ptr_map();

	// Lookup the cache for the specified model in the map.  If it's not found
	// create a new one.
	t_cache_ptr_map::iterator map_iter = cache_ptr_map.find( name );
	if ( map_iter == cache_ptr_map.end() )
	{
		t_shared_ptr< t_cache > cache_ptr( new t_cache( name ) );
		map_iter = cache_ptr_map.insert( std::make_pair( name, cache_ptr ) ).first;
	}

	return *map_iter->second;
}

// --------------------------------------------------------
// Map the adventure actor model name if the name
// specified is an alias
// --------------------------------------------------------
std::string map_adv_actor_model_name( std::string const & name )
{
	static std::string const k_prefix = t_resource_traits< t_adv_actor_model_definition >::prefix;
	static std::string const k_extension = ".h4d";

	static std::string::size_type const k_prefix_size = k_prefix.size();
	static std::string::size_type const k_extension_size = k_extension.size();

	// Construct the resource name
	std::string resource_name = k_prefix;
	resource_name += '.';
	resource_name += name;
	resource_name += k_extension;

	// Lookup the resource
	t_resource_entry const * resource_entry_ptr = find_resource( resource_name );

	// If the resource was not found or the specified name is not an alias simply return the specified name
	if ( resource_entry_ptr == 0 || string_insensitive_compare( resource_name, resource_entry_ptr->name ) == 0 )
		return name;

	resource_name = resource_entry_ptr->name;
	std::string::size_type resource_name_size = resource_name.size();

	if (	resource_name.size() <= k_prefix_size + 1 + k_extension_size
		||	resource_name.data()[ k_prefix_size ] != '.'
		||	string_insensitive_compare( k_prefix, std::string( resource_name, 0, k_prefix_size ) ) != 0
		||	string_insensitive_compare( k_extension, std::string( resource_name, resource_name_size - k_extension_size, k_extension_size ) ) != 0 )
		return name;

	// Strip the extension and prefix
	resource_name.erase( resource_name_size - k_extension_size, k_extension_size ); 
	resource_name.erase( 0, k_prefix_size + 1 );

	return resource_name;
}


// --------------------------------------------------------
// --------------------------------------------------------
std::string get_adv_model_name( t_creature_type creature_type )
{
	assert( creature_type >= 0 && creature_type < k_creature_type_count );
	return get_keyword( creature_type );
}

// --------------------------------------------------------
// get an adventure actor model based on a creature id
// --------------------------------------------------------
t_adv_actor_model_ptr get_adv_model( t_creature_type creature )
{
	assert( creature >= 0 && creature < k_creature_type_count );

	static bool                                  initialized = false;
	static t_abstract_cache< t_adv_actor_model > caches[k_creature_type_count];

	if (!initialized)
	{
		t_creature_type index;

		for (index = t_creature_type(0); index != k_creature_type_count; enum_incr(index))
			caches[index] = get_adv_actor_model_cache( get_adv_model_name( index ) );
		initialized = true;
	}
	return caches[creature].get();
}

// --------------------------------------------------------
// --------------------------------------------------------
std::string get_adv_model_name( t_town_type	alignment, bool is_male, bool is_spellcaster )
{
	return "hero." + get_type_keyword( alignment, is_male, is_spellcaster );
}

// --------------------------------------------------------
// get an adventure actor model based on a hero's alignment and traits
// --------------------------------------------------------
t_adv_actor_model_ptr get_adv_model( t_town_type alignment,
								     bool		 is_male,
								     bool		 is_spellcaster )
{
	static bool initialized = false;

	static t_abstract_cache< t_adv_actor_model > caches[k_town_type_count][2][2];
	
	if (!initialized)
	{
		t_town_type       town;
		int               sex;
		int               type;
		std::string       name;

		for (town = t_town_type(0); town < k_town_type_count; enum_incr(town))
		{
			for (sex = 0; sex < 2; sex++)
			{
				for (type = 0; type < 2; type++)
				{
					get_adv_model_name( town, sex != 0, type != 0 ).swap( name );
					caches[town][sex][type] = get_adv_actor_model_cache( name );
				}
			}
		}
		initialized = true;
	}
	return caches[alignment][is_male][is_spellcaster].get();
}
